#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdbool.h>

#include "cli.h"


cmd_manage_t cmd_manage;

void cmd_manage_init(void)
{
    int i=0;
    for(i=0; i< CLI_MAX_CMDS; i++) {
				memset(cmd_manage.cmd_item[i].brief,0x00,128);
        cmd_manage.cmd_item[i].handler = NULL;
				memset(cmd_manage.cmd_item[i].name,0x00,32);
    }

    cmd_manage.registerCmdNum = 0;
}



/*********************************************************************************************
** Function name:           register_cmd
** Descriptions:            注册指令
** input parameters:        cmd: 指令
** output parameters:       none
** Returned value:          命令项
** Created by:              zheh.zhang
** Created date:            2021-09-08
**--------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
**********************************************************************************************/
int register_cmd(cmd_item_t *cmd)
{
    if(cmd_manage.registerCmdNum > CLI_MAX_CMDS) {
        return -1;
    }

    if(cmd == NULL) {
        return -2;
    }

    if(cmd_manage.cmd_item[cmd_manage.registerCmdNum].handler == NULL) {
        strcpy(cmd_manage.cmd_item[cmd_manage.registerCmdNum].brief, cmd->brief);
        strcpy(cmd_manage.cmd_item[cmd_manage.registerCmdNum].name, cmd->name);
        cmd_manage.cmd_item[cmd_manage.registerCmdNum].handler = cmd->handler;

        cmd_manage.registerCmdNum++;
        return 0;
    }

    return -3;

}

/*********************************************************************************************
** Function name:           register_cmd_manage
** Descriptions:            注册指令集合
** input parameters:        cmd_item: 指令集合
                            cmd_num: 指令个数
** output parameters:       none
** Returned value:          命令项
** Created by:              zheh.zhang
** Created date:            2021-09-08
**--------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
**********************************************************************************************/
int register_cmd_manage(cmd_item_t *cmd_item, int cmd_num)
{
    int ret = 0;
    int i=0;

    cmd_manage_init();

    for(i=0; i<cmd_num; i++) {
        ret = register_cmd(&cmd_item[i]);
        if(ret != 0) {
            return -1;
        }
    }

    return 0;
}
 
/*********************************************************************************************
** Function name:           find_cmd
** Descriptions:           查找命令
** input parameters:        keyword: 命令关键字
** output parameters:       none
** Returned value:          命令项
** Created by:              zheh.zhang
** Created date:            2021-09-08
**--------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
**********************************************************************************************/
static const cmd_item_t *find_cmd(const char *keyword)
{
    int i;

    for(i=0; i<CLI_MAX_CMDS; i++) {
        if(!strcasecmp(keyword, cmd_manage.cmd_item[i].name)) {
            return &cmd_manage.cmd_item[i];
        }
    }

    return NULL;
}

/*********************************************************************************************
** Function name:           strsplit
** Descriptions:            字符串分割 - 在源字符串查找出所有由separator指定的分隔符
                                        （如‘,’）并替换成字符串结束符‘\0’形成子串，同时令list
                                        指针列表中的每一个指针分别指向一个子串
                            
** example                  input => s = "abc,123,456,,fb$"
                            separator = ",$"
                            output => s = abc'\0' 123'\0' 456'\0' '\0' fb'\0' '\0'
                            list[0] = "abc"
                            list[1] = "123"
                            list[2] = "456"
                            list[3] = ""
                            list[4] = "fb"
                            list[5] = ""   

** input parameters:        s: 源字符串
                            separator: 分隔字符串
                            list: 字符串指针列表
                            len: 列表长度
** output parameters:       none
** Returned value:          命令项
** Created by:              zheh.zhang
** Created date:            2021-09-08
**--------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
**********************************************************************************************/
static size_t strsplit(char *s, const char *separator, char *list[], size_t len)
{
    size_t count = 0;
     
    if(s == NULL || list == NULL || len == 0) {
        return 0;
    }

    list[count++] = s;
    while (*s && count < len) {
        if(strchr(separator, *s) != NULL) {
            *s = '\0';
            list[count++] = s+1;
        }
        s++;
    }
    
    return count;
}

/*********************************************************************************************
** Function name:           cli_print
** Descriptions:            打印一个格式化字符串到串口控制台
** input parameters:        
** output parameters:       none
** Returned value:          命令项
** Created by:              zheh.zhang
** Created date:            2021-09-08
**--------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
**********************************************************************************************/
static void cli_print(cli_obj_t *obj, const char *format, ...)
{
    va_list args;
    int len;
    char buf[CLI_MAX_CMD_LEN + CLI_MAX_CMD_LEN/2];

    va_start(args, format);
    len = vsnprintf(buf, sizeof(buf), format, args);
    obj->write(buf, len);
}

/*********************************************************************************************
** Function name:           cli_init
** Descriptions:            cli初始化
** input parameters:        
** output parameters:       none
** Returned value:          命令项
** Created by:              zheh.zhang
** Created date:            2021-09-08
**--------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
**********************************************************************************************/
void cli_init(cli_obj_t *obj, const cli_port_t *p)
{
    obj->read = p->read;
    obj->write = p->write;
    obj->print = cli_print;
    obj->enable = true;
}

/* 进入Cli命令模式 */
void cli_enable(cli_obj_t *obj)
{
    obj->enable = true;
}

/* 退出cli命令模式 */
void cli_disable(cli_obj_t *obj) 
{
    obj->enable = false;
}

/* 处理行 */
static void process_line(cli_obj_t *obj)
{
    char *argv[CLI_MAX_ARGS];
    int argc;
    const cmd_item_t *it;

    argc = strsplit(obj->recvbuf, " ,", argv, CLI_MAX_ARGS);
    if((it = find_cmd(argv[0])) == NULL) {
        obj->print(obj, "Unknow command '%s' - try 'help' \r\n", argv[0]);
        return;
    }

    it->handler(obj, argc, argv);
}

/* 执行一行命令 */
void cli_exec_cmd(cli_obj_t *obj, const char *cmd)
{
    snprintf(obj->recvbuf, CLI_MAX_CMD_LEN, "%s", cmd);
    process_line(obj);
}

/* 命令行处理程序 */
void cli_process(cli_obj_t *obj)
{
    int i;

    if(!obj->read || !obj->enable){
        return;
    }

    i = obj->recvcnt;
    obj->recvcnt += obj->read(&obj->recvbuf[i], CLI_MAX_CMD_LEN - 1);
    while (i < obj->recvcnt) {
        if(obj->recvbuf[i] == '\r' || obj->recvbuf[i] == '\n') {
            obj->recvbuf[i] = '\0';
            process_line(obj);
            obj->recvcnt = 0;
        }
        i++;
    }    
}


static int cmd_item_comparer(const void *item1, const void *item2)
{
    cmd_item_t *it1 = *((cmd_item_t **)item1);
    cmd_item_t *it2 = *((cmd_item_t **)item2);

    return strcmp(it1->name, it2->name);
}

int do_help(struct cli_obj *s, int argc, char *argv[])
{
    int i,j;
    
    if(argc == 2) {
        if(find_cmd(argv[1]) != NULL) {
            s->print(s, cmd_manage.cmd_item[0].brief);
            s->print(s, "\r\n");
        }
        return 0;
    }

//    qsort(cmd_manage.cmd_item, CLI_MAX_CMDS, sizeof(cmd_item_t), cmd_item_comparer);
    s->print(s, "\r\n");
    for(i=0; i<cmd_manage.registerCmdNum; i++) {
        s->print(s, cmd_manage.cmd_item[i].name);

        j = strlen(cmd_manage.cmd_item[i].name);
        if(j < 10) {
            j = 10-j;
        }

        while(j--){
            s->print(s, " ");
        }

        s->print(s, "-");
        s->print(s, cmd_manage.cmd_item[i].brief);
        s->print(s, "\r\n");
    }

    return 1;

}



